#include <stdio.h>
#include <stdlib.h>
#include <pthread.h> 
#include <strings.h>
#include "unistd.h"
#include "log_dbg.h"
#define BUFFSIZE 2
struct prodcons
{
    char buff[BUFFSIZE];
    int write_index;
    int read_index;
    pthread_cond_t notempty;
    pthread_cond_t notfull;
    pthread_mutex_t lock;
};
typedef struct 
{
    int flag;
    pthread_cond_t r_wait;
    pthread_mutex_t lock;
}my_test_res_t;
void  init(struct prodcons *pro);
void* producer_func(void* arg);
void* customer_func(void* arg);
void  put(struct prodcons *pro,int data);
int   get(struct prodcons *pro);
 
int test1()
{
    pthread_t producer,customer;
    struct prodcons pro;
 
    init(&pro);
    
    if(pthread_create(&producer,NULL,producer_func,&pro)!=0){
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }
    if(pthread_create(&customer,NULL,customer_func,&pro)!=0){
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }
 
    if(pthread_join(producer,NULL)!=0){
        perror("pthread_join");
        exit(EXIT_FAILURE);
    }
    if(pthread_join(customer,NULL)!=0){
        perror("pthread_join");
        exit(EXIT_FAILURE);
    }
 
    exit(0);
}
 
void init(struct prodcons *pro)
{
    bzero(pro->buff,sizeof(pro->buff));
    pro->write_index=0;
    pro->read_index=0;
    if((pthread_cond_init(&pro->notempty,NULL)!=0)
        ||(pthread_cond_init(&pro->notfull,NULL)!=0)
        ||(pthread_mutex_init(&pro->lock,NULL)!=0)){
        perror("init");
        exit(EXIT_FAILURE);
    }
}
 
void* producer_func(void* arg)
{
    int i;
    for(i=1;i<=5;++i)
    {
        printf("producer sleep 1 second to produce..\n");
        sleep(1);
        printf("put %d to produce\n",i);
        put(arg,i);
    }
    for(i=6;i<=10;++i)
    {
        printf("producer sleep 3 second to produce..\n");
        sleep(3);
        printf("produce:put %d\n",i);
        put(arg,i);
    }
    put(arg, -1);
    printf("producer exit\n");
    pthread_exit(0);
}
 
void* customer_func(void* arg)
{
    int data;
    while(1)
    {
        printf("customer wait 2 second to consumption.\n");
        sleep(2);
        data=get(arg);
        printf("customer:get %d\n",data);
        if(data==-1)
            break;
    }
    printf("customer exit\n");
    pthread_exit(0);
}
 
void put(struct prodcons *pro,int data)
{
    pthread_mutex_lock(&pro->lock);
    //如果满了，等到非满条件变量产生
    while(((pro->write_index+1)%BUFFSIZE) == pro->read_index)
    {
        printf("producer is full,wait not full");
        pthread_cond_wait(&pro->notfull,&pro->lock);
    }
    pro->buff[pro->write_index]=data;
    pro->write_index++;
    if(pro->write_index>=BUFFSIZE)
        pro->write_index=0;
    pthread_cond_signal(&pro->notempty);
    pthread_mutex_unlock(&pro->lock);
}
 
int   get(struct prodcons *pro)
{
    int data;
    pthread_mutex_lock(&pro->lock);
    //如果空了，则等待非空
    while(pro->write_index==pro->read_index)
    {
        printf("customer is empty,wait not empty\n");
        pthread_cond_wait(&pro->notempty,&pro->lock);
    }
    data=pro->buff[pro->read_index];
    pro->read_index++;
    if(pro->read_index>=BUFFSIZE)
        pro->read_index=0;
    pthread_cond_signal(&pro->notfull);
    pthread_mutex_unlock(&pro->lock);
    return data;
}


int my_test_init(my_test_res_t* res)
{
    bzero(res,sizeof(*res));

    if((pthread_cond_init(&res->r_wait,NULL)!=0)
        ||(pthread_mutex_init(&res->lock,NULL)!=0)){
        perror("init");
        exit(EXIT_FAILURE);
    }
}
void* test_thread_A(void* arg)
{
    my_test_res_t* res =(my_test_res_t*)arg;
    if(!res){
        return NULL;
    }
    while(1){
        pthread_mutex_lock(&res->lock);
        while(!res->flag){
            pthread_cond_wait(&res->r_wait,&res->lock);
        }
        res->flag=0;
        log_debug("run!");
        pthread_mutex_unlock(&res->lock);
    }
}
void* test_thread_B(void* arg)
{
    my_test_res_t* res =(my_test_res_t*)arg;
    if(!res){
        return NULL;
    }
    while(1){
        pthread_mutex_lock(&res->lock);
        while(!res->flag){
            pthread_cond_wait(&res->r_wait,&res->lock);
        }
        res->flag=0;
        log_debug("run!");
        pthread_mutex_unlock(&res->lock);
    }
}
void* thread_wake(void* arg)
{
    int x=0;
    my_test_res_t* res =(my_test_res_t*)arg;
    if(!res){
        return NULL;
    }
    while(1){
        scanf("%d",&x);
        pthread_mutex_lock(&res->lock);
        res->flag = x%2;
        if(res->flag ){
            pthread_cond_broadcast(&res->r_wait);
        }else{
            pthread_cond_signal(&res->r_wait);
        }
        pthread_mutex_unlock(&res->lock);
    }
    return NULL;
}
int my_test_main()
{
    pthread_t wake_thread_ID;
    pthread_t thread_wake_A_ID;
    pthread_t thread_wake_B_ID;;
    my_test_res_t res;
    my_test_init(&res);

    if(pthread_create(&thread_wake_A_ID,NULL,test_thread_A,&res)!=0){
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }
    if(pthread_create(&thread_wake_B_ID,NULL,test_thread_B,&res)!=0){
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }
    if(pthread_create(&wake_thread_ID,NULL,thread_wake,&res)!=0){
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }
    
    if(pthread_join(thread_wake_A_ID,NULL)!=0){
        perror("pthread_join");
        exit(EXIT_FAILURE);
    }
    if(pthread_join(thread_wake_B_ID,NULL)!=0){
        perror("pthread_join");
        exit(EXIT_FAILURE);
    }
    if(pthread_join(wake_thread_ID,NULL)!=0){
        perror("pthread_join");
        exit(EXIT_FAILURE);
    }
    return 0;
}
int main()
{
    my_test_main();
    return 0;
}
